package top.yangguangmc.sunshine_anticheat.check.checks.combat;

import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import top.yangguangmc.sunshine_anticheat.check.Check;
import top.yangguangmc.sunshine_anticheat.check.CheckCategory;
import top.yangguangmc.sunshine_anticheat.check.CheckSetting;
import top.yangguangmc.sunshine_anticheat.events.ServerTickEvent;
import top.yangguangmc.sunshine_anticheat.utils.Utils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class AutoWeaponCheck extends Check {
    private final Map<UUID, Long> switchItemTime = new HashMap<>();
    private final Map<UUID, Long> attackEntityTime = new HashMap<>();
    private final Map<UUID, Integer> previousWeaponSlot = new HashMap<>();
    @CheckSetting(configName = "precise-threshold")
    private int threshold = 1;
    @CheckSetting(configName = "min-switch-back-delay")
    private int minBackDelay = 4;

    public AutoWeaponCheck() {
        super("AutoWeapon", "auto-weapon", CheckCategory.COMBAT);
    }

    @EventHandler
    public void onAttack(EntityDamageByEntityEvent event) {
        if (!validateCheckable(event.getDamager())) return;
        Player player = (Player) event.getDamager();
        attackEntityTime.put(player.getUniqueId(), ServerTickEvent.tickId);
        if (player.getItemInHand() == null || !isBestWeapon(player, -1)) return;
        long time = Math.abs(ServerTickEvent.tickId - switchItemTime.getOrDefault(player.getUniqueId(), 0L));
        if (time <= threshold) {
            failCheck(player, time == 0 ? 3 : 1, "Attacked an entity on the same tick as switched to the best weapon. Delta: {} ticks", time);
            switchItemTime.remove(player.getUniqueId());
        }
    }

    @EventHandler
    public void onSwitchItem(PlayerItemHeldEvent event) {
        Player player = event.getPlayer();
        if (!validateCheckable(player)) return;
        switchItemTime.put(player.getUniqueId(), ServerTickEvent.tickId);
        long time1 = Math.abs(ServerTickEvent.tickId - attackEntityTime.getOrDefault(player.getUniqueId(), 0L));
        if (isBestWeapon(player, event.getNewSlot())) {
            previousWeaponSlot.put(player.getUniqueId(), event.getPreviousSlot());
            if (time1 <= threshold) {
                failCheck(player, time1 == 0 ? 3 : 1, "Switched to the best weapon on the same tick as attacked an entity. Delta: {} ticks", time1);
//                attackEntityTime.remove(player.getUniqueId());
            }
        } else {
            if (time1 <= minBackDelay && event.getNewSlot() == previousWeaponSlot.getOrDefault(player.getUniqueId(), -1)) {
                failCheck(player, time1 <= 1 ? 4 : 2, "Switched back to the previous slot too quickly after attacked an entity. Delta: {} ticks", time1);
                previousWeaponSlot.remove(player.getUniqueId());
            }
        }
    }

    private static boolean isBestWeapon(Player player, int slot) {
        float damage = 0;
        ItemStack best = null;
        ItemStack[] hotbar = Utils.getHotbar(player);
        for (ItemStack item : hotbar) {
            if (item == null) continue;
            float damage1 = getDamage(item);
            if (damage1 > damage) {
                damage = damage1;
                best = item;
            }
        }
        if (best == null) return false;
        if (slot != -1)
            return best.equals(player.getInventory().getItem(slot));
        else return best.equals(player.getItemInHand());
    }

    private static float getDamage(ItemStack item) {
        float damage = 0;
        if (item == null) return damage;
        Material material = item.getType();
        if (material.name().contains("SWORD")) damage += 4;
        else if (material.name().contains("AXE") && !material.name().contains("PICKAXE")) damage += 3;
        else if (material.name().contains("PICKAXE")) damage += 2;
        else if (material.name().contains("SPADE")) damage += 1;
        if (material.name().contains("STONE")) damage += 1;
        else if (material.name().contains("IRON")) damage += 2;
        else if (material.name().contains("DIAMOND")) damage += 3;
        else if (material.name().contains("NETHERITE")) damage += 4;    //TODO: add 1.9+ damage

        damage += item.getEnchantmentLevel(Enchantment.DAMAGE_ALL) * 1.25f + item.getEnchantmentLevel(Enchantment.FIRE_ASPECT) * 0.01f;
        return damage;
    }
}
